//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <wintypes.h>
#include <elasys_server.h>
#include <stdio.h>
#include <buf_io.h>
#include <gdivga.h>
#include "framebuffer.h"
#include <ShareMem.h>

extern VideoContext g_videoCtx;
extern ULONG g_VideoPowerMode;
extern BufferCopy g_BufferCopy;

ECode GDIVideo::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode GDIVideo::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ Int32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    return NOERROR;
}

ECode GDIVideo::GetOverlayOut(
        /* [in] */ int layer,
        /* [out] */ MemoryBuf * pData)
{
    ECode ec;

    if (pData->GetCapacity() < (int)sizeof(OverlayFormat)) {
        return E_OUT_OF_MEMORY;
    }

    if(1 == layer) {
        if (NULL == g_videoCtx.pOverlay1Fb) {
            ec = E_ACCESS_DENIED;
            return ec;
        }
        pData->Copy((const Byte *)(&g_videoCtx.overlay1), sizeof(OverlayFormat));
    } else {
        if (NULL == g_videoCtx.pOverlay2Fb) {
            ec = E_ACCESS_DENIED;
            return ec;
        }
        pData->Copy((const Byte *)(&g_videoCtx.overlay2), sizeof(OverlayFormat));
    }

    pData->SetUsed(sizeof(OverlayFormat));

    return NOERROR;
}

ECode GDIVideo::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    char *tBuf = NULL;
    int layer;
    ECode ec = NOERROR;
    if (ppCompletionEvent && *ppCompletionEvent) {
        return E_INVALID_ARGUMENT;
    }

    switch (nControlCode) {
        case 0 :
            //cls
            memset(g_videoCtx.pBasePlaneFb, 0, g_videoCtx.nBasePlaneSize);
            break;
        case 1 ://get frame buffer physical address
        /* LCD   PhyscalBase | width | height | bitspp
        BITS64 ......   32  ... 20  ...   8  ...   0
        */
            assert(NULL != (char *)pOutBuffer);
            tBuf = (char*)pOutBuffer;
            *(UINT32*)tBuf = (( g_videoCtx.nLCDWidth & 0xfff ) << 20) |
                            ((g_videoCtx.nLCDHeight & 0xfff) << 8) |
                            (g_videoCtx.nBasePlanePixelBits & 0xff);

            ((UINT32*)tBuf)[1] = (UINT32)g_videoCtx.pBasePlaneFb;
            pOutBuffer->SetUsed(sizeof(UINT32)*2);
            break;
        case 2:
            // Compatible with init mode for x86
            break;

        case 3:{
            // Get base plane frame buffer virtual address
            void    *pBasePlaneFrameBuf;

            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            if (pOutBuffer->GetCapacity() < (int)sizeof(UINT32)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            pBasePlaneFrameBuf = Video_InitBasePlaneFramebuffer();
            if (!pBasePlaneFrameBuf) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            *(UINT32 *)pOutBuffer->GetPayload() = (UINT32)pBasePlaneFrameBuf;
            pOutBuffer->SetUsed(sizeof(UINT32));
            break;
        }
        case 4:{
            // Get base plane frame buffer virtual address
            void    *pBasePlaneFrameBuf;

            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            if (pOutBuffer->GetCapacity() < (int)sizeof(UINT32)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            pBasePlaneFrameBuf = Video_InitBasePlaneFramebuffer();
            if (!pBasePlaneFrameBuf) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            ec = ConvertAllocAddrToId(
                    (Address)pBasePlaneFrameBuf,
                    0,
                    (unsigned long *)pOutBuffer->GetPayload());
            if (FAILED(ec)) {
                break;
            }
            pOutBuffer->SetUsed(sizeof(UINT32));
            break;
        }
        case 6:
            // Compatible with releasing frame buffer for x86
            break;

        case 7:{
            // Get baseplane format
            FrameBufferFormat fbFormat;

            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            if (pOutBuffer->GetCapacity() < (int)sizeof(FrameBufferFormat)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }
            fbFormat.Color = g_videoCtx.BasePlaneColorFormat;
            fbFormat.Bpp = g_videoCtx.nBasePlanePixelBits;
            fbFormat.Width = g_videoCtx.nLCDWidth;
            fbFormat.Height = g_videoCtx.nLCDHeight;
            pOutBuffer->Copy((Byte *)&fbFormat, sizeof(FrameBufferFormat));
            break;
        }

        case 8:
            // Set overlay, if success also return the current format
            OverlayFormat *pOverlayFormat;


            if (inBuffer.GetCapacity() < (int)sizeof(OverlayFormat)) {
                ec = E_OUT_OF_MEMORY;
                break;
            }

            pOverlayFormat = (OverlayFormat *)inBuffer.GetPayload();

            if (!Video_SetOverlay(pOverlayFormat)) {
                ec = E_ACCESS_DENIED;
                break;
            }
            if (NOERROR == ec) {
                ec = GetOverlayOut(pOverlayFormat->Layer, pOutBuffer);
            }
            break;
        case 9:
            if (inBuffer.GetPayload() == NULL) layer = 2; // default: overlay2
            else layer = *(int *)inBuffer.GetPayload();
            // Get overlay
            ec = GetOverlayOut(layer, pOutBuffer);
            break;

        case 10: {
            // Disable overlay
            int *pBaseFmt = (int *)(char *)pOutBuffer;
            if (inBuffer.GetPayload() == NULL) layer = 2; // default: overlay2
            else layer = (int)*(USHORT *)(char *)inBuffer.GetPayload();
            Video_DisableOverlay(layer);
            if (pBaseFmt) {
                *pBaseFmt = g_videoCtx.BasePlaneColorFormat;
                pOutBuffer->SetUsed(sizeof(int));
            }
        }
            break;

        case 11://conflict with arm video driver.change to 101 better.
            // Render
            Video_Render();
            break;

        case 12://102 better
            //AutoRender
            Video_AutoRender();
            break;

        case 14:
        {
            BYTE transLvl;

            if (inBuffer.GetPayload() == NULL) transLvl = 255; // default
            else transLvl = *(BYTE *)inBuffer.GetPayload();

            Video_SetTransLevel(transLvl);

            break;
        }

        case 17:{
            int iLevel = *(int *)inBuffer.GetPayload();
            printf("SetBackLight, Level %d\n", iLevel);
            break;
        }

        //1000-1004 for power management.
        case 1000:{
//            g_videoCtx.pfnBufferCopy=g_BufferCopy.PowerOffCopy;
            g_VideoPowerMode=0;
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            assert(pOutBuffer->GetCapacity() >= (int)sizeof(ULONG));
            pOutBuffer->Copy((Byte *)&g_VideoPowerMode,sizeof(ULONG));
            break;
        }
        case 1001:
        case 1002:
        case 1003:{
//            g_videoCtx.pfnBufferCopy=g_BufferCopy.DarkenCopy;
            g_VideoPowerMode=nControlCode-1000;
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            assert(pOutBuffer->GetCapacity() >= (int)sizeof(ULONG));
            pOutBuffer->Copy((Byte *)&g_VideoPowerMode,sizeof(ULONG));
            break;
        }
        case 1004:{
//            g_videoCtx.pfnBufferCopy=g_BufferCopy.NormalCopy;
            g_VideoPowerMode=nControlCode-1000;
            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            assert(pOutBuffer->GetCapacity() >= (int)sizeof(ULONG));
            pOutBuffer->Copy((Byte *)&g_VideoPowerMode,sizeof(ULONG));
            break;
        }
        default:
            ec = E_NOT_IMPLEMENTED;
            break;
    }

    return ec;
}

IDeviceDriver * CreateDisplay(uint_t uDeviceNo, void *pvParameter)
{
    if(!Video_InitVideo()){
        return NULL;
    }
    static GDIVideo Gdivga;
    return (IDeviceDriver *)&Gdivga;
}
